package com.zhiziai.aiproxy.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.MessageConstraints;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParser;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.io.HttpMessageParser;
import org.apache.http.io.HttpMessageParserFactory;
import org.apache.http.io.HttpMessageWriterFactory;
import org.apache.http.io.SessionInputBuffer;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicLineParser;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.message.LineParser;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author huangjianxiong
 */
@Slf4j
public class HttpClientUtil {

    public static final HttpClientUtil singleton = new HttpClientUtil();
    private static CloseableHttpClient httpClient;
    private static final Charset charset = Consts.UTF_8;

    static {
        initHttpClient();
    }

    private static void initHttpClient() {
        // 自定义http响应报文包装器
        HttpMessageParserFactory<HttpResponse> responseParserFactory = new DefaultHttpResponseParserFactory() {
            @Override
            public HttpMessageParser<HttpResponse> create(SessionInputBuffer buffer, MessageConstraints constraints) {
                LineParser lineParser = new BasicLineParser() {
                    @Override
                    public Header parseHeader(final CharArrayBuffer buffer) {
                        try {
                            return super.parseHeader(buffer);
                        } catch (ParseException ex) {
                            return new BasicHeader(buffer.toString(), null);
                        }
                    }

                };
                return new DefaultHttpResponseParser(buffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints) {
                    @Override
                    protected boolean reject(final CharArrayBuffer line, int count) {
                        // 这里可以拒绝一些特殊返回值
                        return false;
                    }

                };
            }

        };

        HttpMessageWriterFactory<HttpRequest> requestWriterFactory = new DefaultHttpRequestWriterFactory();

        org.apache.http.conn.HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(requestWriterFactory, responseParserFactory);

        SSLContext sslContext = SSLContexts.createSystemDefault();

        // 允许访问任意hostname
        X509HostnameVerifier allowAllHostNameVerifier = new AllowAllHostnameVerifier();

        // 注册http和https两种协议
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new SSLConnectionSocketFactory(sslContext, allowAllHostNameVerifier))
                .build();

        // 使用自定义DNS解析
        DnsResolver dnsResolver = new SystemDefaultDnsResolver() {
            @Override
            public InetAddress[] resolve(final String host) throws UnknownHostException {
                return super.resolve(host);
            }

        };

        // 根据自定义的配置，生成连接池管理器
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connFactory, dnsResolver);

        // 设置默认socket配置
        SocketConfig socketConfig = SocketConfig.custom()
                .setTcpNoDelay(true)
                .build();

        connManager.setDefaultSocketConfig(socketConfig);

        // http报文大小限制
        MessageConstraints messageConstraints = MessageConstraints.custom()
                .setMaxHeaderCount(200)
                .setMaxLineLength(2000)
                .build();

        // 连接属性配置
        ConnectionConfig connectionConfig = ConnectionConfig.custom()
                .setMalformedInputAction(CodingErrorAction.IGNORE)
                .setUnmappableInputAction(CodingErrorAction.IGNORE)
                .setCharset(charset)
                .setMessageConstraints(messageConstraints)
                .build();

        connManager.setDefaultConnectionConfig(connectionConfig);
        connManager.setMaxTotal(100);
        connManager.setDefaultMaxPerRoute(20);

        // 默认请求配置
        RequestConfig requestConfig = RequestConfig.custom()
                .setCookieSpec(CookieSpecs.BEST_MATCH)
                .setExpectContinueEnabled(true)
                .setStaleConnectionCheckEnabled(true)
                .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
                .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
                .setSocketTimeout(8000)
                .setConnectTimeout(2000)
                .setConnectionRequestTimeout(5000)
                .build();

        httpClient = HttpClients.custom()
                .setConnectionManager(connManager)
                .setDefaultRequestConfig(requestConfig)
                .setUserAgent("Passport-Webclient 1.0.6")
                .build();

    }

    private static CloseableHttpClient getHttpClient() {
        return httpClient;
    }

    public String doGet(String url, Map<String, String> params) {
        return doGet(url, params, false, null, null);
    }

    public String doGet(String urlp, Map<String, String> params, boolean writeCookie, HttpServletRequest request, HttpServletResponse response) {
        String url = urlp;
        String result = "";
        CloseableHttpClient httpClient = getHttpClient();
        HttpGet httpGet = null;
        CloseableHttpResponse httpResponse = null;
        try {
            if (params != null) {
                Iterator<String> keys = params.keySet().iterator();
                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
                while (keys.hasNext()) {
                    String key = keys.next();
                    nvps.add(new BasicNameValuePair(key, params.get(key)));
                }
                String queryString = URLEncodedUtils.format(nvps, charset.displayName());
                url = url + (url.contains("?") ? "&" : "?") + queryString;
            }
            log.debug("http request url: {}", url);
            httpGet = new HttpGet(url);

            httpResponse = httpClient.execute(httpGet);
            result = EntityUtils.toString(httpResponse.getEntity(), charset);
        } catch (Exception e) {
            httpGet.abort();
            log.error(e.getMessage());
        } finally {
            try {
                if (httpResponse != null) {
                    httpResponse.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage());

            }
        }
        return result;
    }

    public String doPost(String url, Map<String, String> params) {
        return doPost(url, params, false, null, null);
    }

    public String doPost(String url, Map<String, String> params, boolean writeCookie, HttpServletRequest request, HttpServletResponse response) {
        String result = "";
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse httpResponse = null;
        try {
            if (params != null) {
                Iterator<String> keys = params.keySet().iterator();
                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
                while (keys.hasNext()) {
                    String key = keys.next();
                    nvps.add(new BasicNameValuePair(key, params.get(key)));
                }
                httpPost.setEntity(new UrlEncodedFormEntity(nvps, charset));
                //设置请求头部
                httpPost.setHeader("Content-Type", "application/json");
            }
            httpResponse = httpClient.execute(httpPost);

            if (httpResponse.getStatusLine().getStatusCode() != 200)
                throw new Exception("error http code : " + httpResponse.getStatusLine().getStatusCode());

            result = EntityUtils.toString(httpResponse.getEntity(), charset);
            if (writeCookie)
                writeCookie(httpResponse, false, request, response);

        } catch (Exception e) {
            httpPost.abort();
            log.error(e.getMessage());

        } finally {
            try {
                if (httpResponse != null) {
                    httpResponse.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        return result;
    }

    private void writeCookie(HttpResponse httpResponse, boolean autoLogin, HttpServletRequest request, HttpServletResponse response) {
        Header[] headers = httpResponse.getAllHeaders();
        String domain = "58.com";
        if (headers != null && headers.length > 0) {
            for (Header header : headers) {
                if (header.getName().equals("Set-Cookie")) {
                    String cookieName = header.getValue().substring(0, header.getValue().indexOf("="));
                    String cookieValue = header.getValue().substring(header.getValue().indexOf("=") + 1).replaceAll("\"", "").split(" ")[0];
                    if (cookieValue.endsWith(";"))
                        cookieValue = cookieValue.substring(0, cookieValue.length() - 1);

                    if ("58passport".equals(cookieName))
                        continue;

                    Cookie cookie = new Cookie(cookieName, cookieValue);
                    cookie.setDomain(domain);
                    cookie.setPath("/");
                    if (autoLogin) {
                        cookie.setMaxAge(2629743);
                    }
                    response.addCookie(cookie);
                }
            }
        }
    }

    public String doPostByRaw(String url,String param){
        try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
            final HttpPost httpPost = new HttpPost(url);
            //设置请求体参数
            StringEntity entity = new StringEntity(param);
            entity.setContentEncoding("utf-8");
            httpPost.setEntity(entity);
            //设置请求头部
            httpPost.setHeader("Content-Type", "application/json");
            //执行请求，返回请求响应
            try (final CloseableHttpResponse response = httpClient.execute(httpPost)) {
                //请求返回状态码
                int statusCode = response.getStatusLine().getStatusCode();
                //请求成功
                if (statusCode == HttpStatus.SC_OK && statusCode <= HttpStatus.SC_TEMPORARY_REDIRECT) {
                    //取出响应体
                    final HttpEntity entity2 = response.getEntity();
                    //从响应体中解析出token
                    return EntityUtils.toString(entity2, "utf-8");
                } else {
                    //请求失败
                    throw new ClientProtocolException("请求失败，响应码为：" + statusCode);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}
